﻿using System;
using System.Collections.Generic;
using System.DirectoryServices.AccountManagement;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;

namespace AccessBasedDesktop.Core
{
    /// <summary>
    ///     Manager options class
    /// </summary>
    [Serializable]
    public class Options
    {
        private string excludedRoles;
        private List<string> excludedRolesList;
        private string excludedUsers;
        private List<string> excludedUsersList;

        public Options()
        {
            JornalEventCodes = @"4720-4767";
            TimerInterval = 0;
            DomainName = "";
            DesktopSourceDir = "";
            WatchForChages = true;
            excludedRoles = "";
            excludedUsers = "";
        }

        /// <summary>
        /// FileSystem watcher usage flag
        /// </summary>
        public bool WatchForChages { get; set; }

        /// <summary>
        /// Etalon desktop directory
        /// </summary>
        public string DesktopSourceDir { get; set; }

        /// <summary>
        /// Security log event codes for tracking
        /// </summary>
        public string JornalEventCodes { get; set; }

        /// <summary>
        /// Whether manager should track event log
        /// </summary>
        public bool UseJornal
        {
            get { return !string.IsNullOrEmpty(JornalEventCodes); }
        }

        /// <summary>
        /// Whether timer update active
        /// </summary>
        public bool ScanOnTimer
        {
            get { return TimerInterval > 0; }
        }

        /// <summary>
        /// Update timer interval in seconds
        /// </summary>
        public int TimerInterval { get; set; }

        /// <summary>
        /// Whether manager should use active directory domain
        /// </summary>
        public bool UseDomain
        {
            get { return !string.IsNullOrEmpty(DomainName); }
        }

        /// <summary>
        /// AD domain name
        /// </summary>
        public string DomainName { get; set; }

        /// <summary>
        /// Whether manager should use active directory domain
        /// </summary>
        public bool UseActiveDirectory
        {
            get { return !string.IsNullOrEmpty(DomainName); }
        }

        /// <summary>
        /// Convert excluded users string to list
        /// </summary>
        internal List<string> ExcludedUsersList
        {
            get
            {
                if (excludedUsersList == null)
                {
                    excludedUsersList = new List<string>();
                    if (!string.IsNullOrEmpty(ExcludedUsers))
                    {
                        excludedUsersList.AddRange(ExcludedUsers.Split(','));
                    }
                }
                return excludedUsersList;
            }
        }

        /// <summary>
        /// Convert excluded roles string to list
        /// </summary>
        internal List<string> ExcludedRolesList
        {
            get
            {
                if (excludedRolesList == null)
                {
                    excludedRolesList = new List<string>();
                    if (!string.IsNullOrEmpty(ExcludedRoles))
                    {
                        excludedRolesList.AddRange(ExcludedRoles.Split(','));
                    }
                }
                return excludedRolesList;
            }
        }

        /// <summary>
        /// Computer name
        /// </summary>
        internal string MachineName
        {
            get { return UseDomain ? DomainName : Environment.MachineName; }
        }

        /// <summary>
        /// Security context type
        /// </summary>
        internal ContextType ContextType
        {
            get { return UseDomain ? ContextType.Domain : ContextType.Machine; }
        }

        /// <summary>
        /// Excluded users string
        /// </summary>
        public string ExcludedUsers
        {
            get { return excludedUsers; }
            set
            {
                excludedUsers = value;
                excludedUsersList = null;
            }
        }

        /// <summary>
        /// Excluded roles string
        /// </summary>
        public string ExcludedRoles
        {
            get { return excludedRoles; }
            set
            {
                excludedRoles = value;
                excludedRolesList = null;
            }
        }

        /// <summary>
        /// Returns default options file name (located in entry assembly directory)
        /// </summary>
        public static string DefaultOptionsFileName
        {
            get { return Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), @"Options.xml"); }
        }

        /// <summary>
        /// Get current desktop source full path
        /// </summary>
        /// <returns></returns>
        internal string GetCurrentDesktopDir()
        {
            string result = string.IsNullOrEmpty(DesktopSourceDir)
                ? Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), @"Desktop")
                : DesktopSourceDir;

            result = Environment.ExpandEnvironmentVariables(result);

            result = Path.GetFullPath(result);

            if (!Directory.Exists(result))
                Directory.CreateDirectory(result);

            return result;
        }

        /// <summary>
        /// Whether code should raise desktops update
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        internal bool IsValidEventCode(long code)
        {
            var sqCodes = JornalEventCodes.Split(',');
            var result = false;
            foreach (var sqCode in sqCodes)
            {
                if (sqCode.Contains("-"))
                {
                    var rangeCodes = sqCode.Split('-');
                    var loBound = long.Parse(rangeCodes[0]);
                    var hiBound = long.MaxValue;
                    if (rangeCodes.Length > 1)
                    {
                        hiBound = long.Parse(rangeCodes[1]);
                    }
                    result = code >= loBound && code <= hiBound;
                }
                else
                {
                    result = code == long.Parse(sqCode);
                }
            }
            return result;
        }

        /// <summary>
        /// Load options from xml file
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public static Options LoadFromFile(string fileName = null)
        {
            if (string.IsNullOrEmpty(fileName))
                fileName = DefaultOptionsFileName;

            var result = new Options();

            if (File.Exists(fileName))
            {
                var xml = new XmlSerializer(typeof (Options));
                using (var stream = new FileStream(fileName, FileMode.Open))
                {
                    result = (Options) xml.Deserialize(stream);
                }
            }
            return result;
        }

        /// <summary>
        /// Save options to xml file
        /// </summary>
        /// <param name="fileName"></param>
        public void SaveToFile(string fileName = null)
        {
            if (string.IsNullOrEmpty(fileName))
                fileName = DefaultOptionsFileName;

            var xml = new XmlSerializer(typeof (Options));
            using (var stream = new FileStream(fileName, FileMode.Create))
            {
                xml.Serialize(stream, this);
            }
        }
    }
}